package org.qiyu.live.user.provider.rpc;

import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.rocketmq.client.producer.MQProducer;
import org.apache.rocketmq.common.message.Message;
import org.idea.qiyu.live.framework.redis.starter.key.UserProviderCacheKeyBuilder;
import org.qiyu.live.common.interfaces.utils.ConvertBeanUtils;
import org.qiyu.live.user.constants.CacheAsyncDeleteCode;
import org.qiyu.live.user.constants.UserPeoviderTopicNames;
import org.qiyu.live.user.constants.UserTagFieldNameConstants;
import org.qiyu.live.user.constants.UserTagsEnum;
import org.qiyu.live.user.dto.UserCacheAsyncDeleteDTO;
import org.qiyu.live.user.dto.UserTagDTO;
import org.qiyu.live.user.interfaces.IUserTagRpc;
import org.qiyu.live.user.provider.dao.mapper.IUserTagMapper;
import org.qiyu.live.user.provider.dao.po.UserTagPO;
import org.qiyu.live.user.provider.utils.TagInfoUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * @ClassName: UserTagsEnumImpl
 * @Description: TODO
 * @Author zhangguangyan
 * @Date 2023/08/05/ 14:06:51
 * @Version 1.0
 */
@DubboService
public class UserTagsEnumImpl implements IUserTagRpc {
    @Resource
    private IUserTagMapper iUserTagMapper;

    @Resource
    private RedisTemplate<String, UserTagDTO> redisTemplate;

    @Resource
    private UserProviderCacheKeyBuilder cacheKeyBuilder;
    @Resource
   private MQProducer mqProducer;

    @Override
    /**
     * @Description: 选择标签  新增标签
     * @Param: [userId, userTagsEnum]
     * @return: boolean
     */
    public boolean setTag(Long userId, UserTagsEnum userTagsEnum) {
        //userTagsEnum.getTag() 2的n次方
        //尝试 update true return
        //设置了标签 没有记录(两种失败场景)
        //select is null  ,insert return , update
        boolean updateStatus = iUserTagMapper.setTag(userId, userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        if (updateStatus) {
            //删除缓存
            deleteUserTagFromRedis(userId);
            //更新成功
            return true;
        }
        String setNxKey = cacheKeyBuilder.buildTagLockKey(userId);
        //当有分布式或者多线程的情况下  会出现并发问题
        String setBxResult = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                //获取key的序列化方式
                RedisSerializer keySerializer = redisTemplate.getKeySerializer();
                // 获取value的序列化方式
                RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
                return (String) connection.execute("set", keySerializer.serialize(setNxKey),
                        //  -1 代表永不过期
                        valueSerializer.serialize("-1"),
                        // 设置nx ex 3 三个参数 nx 不存在才设置 ex 3秒过期 3秒后自动删除 保证原子性 防止死锁
                        "NX".getBytes(StandardCharsets.UTF_8),
                        "EX".getBytes(StandardCharsets.UTF_8),
                        "3".getBytes(StandardCharsets.UTF_8));

            }
        });
        if (!"ok".equals(setBxResult)){
            return  false;
        }
        UserTagPO userTagPo = iUserTagMapper.selectById(userId);
        if (userTagPo != null) {
            //有记录
            return false;
        }
        userTagPo = new UserTagPO();
        userTagPo.setUserId(userId);
        iUserTagMapper.insert(userTagPo);
        updateStatus = iUserTagMapper.setTag(userId, userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        redisTemplate.delete(setNxKey);
        return updateStatus;

    }

    @Override
    /**
     * @Description: 取消标签 删除标签
     */
    public boolean cancelTag(Long userId, UserTagsEnum userTagsEnum) {
        Boolean userTag = iUserTagMapper.cancelTag(userId, userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        if (!userTag) {
            return false;
        }
        //删除缓存
        deleteUserTagFromRedis(userId);
        return true;
    }

    @Override
    /**
     * @Description: 判断是否包含标签
     */
    public boolean containTag(Long userId, UserTagsEnum userTagsEnum) {
        // 先查询记录
        UserTagDTO userTagDTO = this.queryByUserIdFromRedis(userId);
        //如果它等于null 则 直接返回false
        if (userTagDTO == null) return false;
        //如果不等于null 则判断是否包含标签
        String fieldName = userTagsEnum.getFieldName();
        if (UserTagFieldNameConstants.TAG_INFO_01.equals(fieldName)) {
            return TagInfoUtils.isContain(userTagDTO.getTagInfo01(), userTagsEnum.getTag());
        } else if (UserTagFieldNameConstants.TAG_INFO_02.equals(fieldName)) {
            return TagInfoUtils.isContain(userTagDTO.getTagInfo02(), userTagsEnum.getTag());
        } else if (UserTagFieldNameConstants.TAG_INFO_03.equals(fieldName)) {
            return TagInfoUtils.isContain(userTagDTO.getTagInfo03(), userTagsEnum.getTag());
        }
        return false;
    }

    /**
     * @Description:  从redis中删除用户标签对象
     * @param userId
     */
    private void deleteUserTagFromRedis(Long userId) {
        String key = cacheKeyBuilder.buildTagKey(userId);
        redisTemplate.delete(key);
        HashMap<String, Object> map = new HashMap() {{
            put("userId", userId);
        }};
        UserCacheAsyncDeleteDTO.builder()
                .code(CacheAsyncDeleteCode.USER_TAG_DELETE.getCode())
                .json(JSON.toJSONString(map)).build();
        Message message = new Message() {{
            setTopic(UserPeoviderTopicNames.CACHE_ASYNC_DELETE_TOPIC);
            setBody(userId.toString().getBytes());
        }};
        //延迟一秒进行缓存的二次删除
        message.setDelayTimeLevel(1);
        try {
            mqProducer.send(message);
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    /**
     * @Params @param userId 用户id
     * @Return @return {@link UserTagDTO }
     * @Description 查询redis 是否有缓存 有则直接返回 没有则查询数据库
     * @Author zhangguangyan
     * @Date 2023-08-12 00:32:39
     */
    private UserTagDTO queryByUserIdFromRedis(Long userId) {
        //获取key值
        String redisKey = cacheKeyBuilder.buildTagKey(userId);
        UserTagDTO userTagDTO = redisTemplate.opsForValue().get(redisKey);
        if (userTagDTO != null) {
            return userTagDTO;
        }
        UserTagPO userTagPo = iUserTagMapper.selectById(userId);
        if (userTagPo == null) {
            return null;
        }
        userTagDTO = ConvertBeanUtils.convert(userTagPo, UserTagDTO.class);
        redisTemplate.opsForValue().set(redisKey, userTagDTO);
        return userTagDTO;
    }

}
